/*
Copyright (C) 2014 Infinite Automation Systems Inc. All rights reserved.
@author Matthew Lohbihler
*/
package com.serotonin.m2m2.maintenanceEvents;
import java.io.IOException;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import com.serotonin.ShouldNeverHappenException;
import com.serotonin.json.JsonException;
import com.serotonin.json.JsonReader;
import com.serotonin.json.ObjectWriter;
import com.serotonin.json.spi.JsonProperty;
import com.serotonin.json.spi.JsonSerializable;
import com.serotonin.json.type.JsonObject;
import com.serotonin.m2m2.Common;
import com.serotonin.m2m2.db.dao.DataSourceDao;
import com.serotonin.m2m2.i18n.ProcessResult;
import com.serotonin.m2m2.i18n.TranslatableJsonException;
import com.serotonin.m2m2.i18n.TranslatableMessage;
import com.serotonin.m2m2.rt.event.AlarmLevels;
import com.serotonin.m2m2.rt.event.type.AuditEventType;
import com.serotonin.m2m2.util.ChangeComparable;
import com.serotonin.m2m2.util.ExportCodes;
import com.serotonin.m2m2.vo.dataSource.DataSourceVO;
import com.serotonin.m2m2.vo.event.EventTypeVO;
import com.serotonin.m2m2.web.taglib.Functions;
import com.serotonin.timer.CronTimerTrigger;
import com.serotonin.validation.StringValidation;
public class MaintenanceEventVO implements ChangeComparable<MaintenanceEventVO>, JsonSerializable {
public static final String XID_PREFIX = "ME_";
public static final int TYPE_MANUAL = 1;
public static final int TYPE_HOURLY = 2;
public static final int TYPE_DAILY = 3;
public static final int TYPE_WEEKLY = 4;
public static final int TYPE_MONTHLY = 5;
public static final int TYPE_YEARLY = 6;
public static final int TYPE_ONCE = 7;
public static final int TYPE_CRON = 8;
public static ExportCodes TYPE_CODES = new ExportCodes();
static {
TYPE_CODES.addElement(TYPE_MANUAL, "MANUAL", "maintenanceEvents.type.manual");
TYPE_CODES.addElement(TYPE_HOURLY, "HOURLY", "maintenanceEvents.type.hour");
TYPE_CODES.addElement(TYPE_DAILY, "DAILY", "maintenanceEvents.type.day");
TYPE_CODES.addElement(TYPE_WEEKLY, "WEEKLY", "maintenanceEvents.type.week");
TYPE_CODES.addElement(TYPE_MONTHLY, "MONTHLY", "maintenanceEvents.type.month");
TYPE_CODES.addElement(TYPE_YEARLY, "YEARLY", "maintenanceEvents.type.year");
TYPE_CODES.addElement(TYPE_ONCE, "ONCE", "maintenanceEvents.type.once");
TYPE_CODES.addElement(TYPE_CRON, "CRON", "maintenanceEvents.type.cron");
}
private int id = Common.NEW_ID;
private String xid;
private int dataSourceId;
@JsonProperty
private String alias;
private int alarmLevel = AlarmLevels.NONE;
private int scheduleType = TYPE_MANUAL;
@JsonProperty
private boolean disabled = false;
@JsonProperty
private int activeYear;
@JsonProperty
private int activeMonth;
@JsonProperty
private int activeDay;
@JsonProperty
private int activeHour;
@JsonProperty
private int activeMinute;
@JsonProperty
private int activeSecond;
@JsonProperty
private String activeCron;
@JsonProperty
private int inactiveYear;
@JsonProperty
private int inactiveMonth;
@JsonProperty
private int inactiveDay;
@JsonProperty
private int inactiveHour;
@JsonProperty
private int inactiveMinute;
@JsonProperty
private int inactiveSecond;
@JsonProperty
private String inactiveCron;
//
//
// Convenience data from data source
//
private String dataSourceTypeId;
private String dataSourceName;
private String dataSourceXid;
public boolean isNew() {
return id == Common.NEW_ID;
}
@Override
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getXid() {
return xid;
}
public void setXid(String xid) {
this.xid = xid;
}
public int getDataSourceId() {
return dataSourceId;
}
public void setDataSourceId(int dataSourceId) {
this.dataSourceId = dataSourceId;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public int getAlarmLevel() {
return alarmLevel;
}
public void setAlarmLevel(int alarmLevel) {
this.alarmLevel = alarmLevel;
}
public int getScheduleType() {
return scheduleType;
}
public void setScheduleType(int scheduleType) {
this.scheduleType = scheduleType;
}
public boolean isDisabled() {
return disabled;
}
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
public int getActiveYear() {
return activeYear;
}
public void setActiveYear(int activeYear) {
this.activeYear = activeYear;
}
public int getActiveMonth() {
return activeMonth;
}
public void setActiveMonth(int activeMonth) {
this.activeMonth = activeMonth;
}
public int getActiveDay() {
return activeDay;
}
public void setActiveDay(int activeDay) {
this.activeDay = activeDay;
}
public int getActiveHour() {
return activeHour;
}
public void setActiveHour(int activeHour) {
this.activeHour = activeHour;
}
public int getActiveMinute() {
return activeMinute;
}
public void setActiveMinute(int activeMinute) {
this.activeMinute = activeMinute;
}
public int getActiveSecond() {
return activeSecond;
}
public void setActiveSecond(int activeSecond) {
this.activeSecond = activeSecond;
}
public String getActiveCron() {
return activeCron;
}
public void setActiveCron(String activeCron) {
this.activeCron = activeCron;
}
public int getInactiveYear() {
return inactiveYear;
}
public void setInactiveYear(int inactiveYear) {
this.inactiveYear = inactiveYear;
}
public int getInactiveMonth() {
return inactiveMonth;
}
public void setInactiveMonth(int inactiveMonth) {
this.inactiveMonth = inactiveMonth;
}
public int getInactiveDay() {
return inactiveDay;
}
public void setInactiveDay(int inactiveDay) {
this.inactiveDay = inactiveDay;
}
public int getInactiveHour() {
return inactiveHour;
}
public void setInactiveHour(int inactiveHour) {
this.inactiveHour = inactiveHour;
}
public int getInactiveMinute() {
return inactiveMinute;
}
public void setInactiveMinute(int inactiveMinute) {
this.inactiveMinute = inactiveMinute;
}
public int getInactiveSecond() {
return inactiveSecond;
}
public void setInactiveSecond(int inactiveSecond) {
this.inactiveSecond = inactiveSecond;
}
public String getInactiveCron() {
return inactiveCron;
}
public void setInactiveCron(String inactiveCron) {
this.inactiveCron = inactiveCron;
}
public String getDataSourceTypeId() {
return dataSourceTypeId;
}
public void setDataSourceTypeId(String dataSourceTypeId) {
this.dataSourceTypeId = dataSourceTypeId;
}
public String getDataSourceName() {
return dataSourceName;
}
public void setDataSourceName(String dataSourceName) {
this.dataSourceName = dataSourceName;
}
public String getDataSourceXid() {
return dataSourceXid;
}
public void setDataSourceXid(String dataSourceXid) {
this.dataSourceXid = dataSourceXid;
}
public EventTypeVO getEventType() {
return new EventTypeVO(MaintenanceEventType.TYPE_NAME, null, id, 0, getDescription(), alarmLevel);
}
public TranslatableMessage getDescription() {
TranslatableMessage message;
if (!StringUtils.isBlank(alias))
message = new TranslatableMessage("common.default", alias);
else if (scheduleType == TYPE_MANUAL)
message = new TranslatableMessage("maintenanceEvents.schedule.manual", dataSourceName);
else if (scheduleType == TYPE_ONCE) {
message = new TranslatableMessage("maintenanceEvents.schedule.onceUntil", dataSourceName,
Functions.getTime(new DateTime(activeYear, activeMonth, activeDay, activeHour, activeMinute,
activeSecond, 0).getMillis()), Functions.getTime(new DateTime(inactiveYear, inactiveMonth,
inactiveDay, inactiveHour, inactiveMinute, inactiveSecond, 0).getMillis()));
}
else if (scheduleType == TYPE_HOURLY) {
String activeTime = StringUtils.leftPad(Integer.toString(activeMinute), 2, '0') + ":"
+ StringUtils.leftPad(Integer.toString(activeSecond), 2, '0');
message = new TranslatableMessage("maintenanceEvents.schedule.hoursUntil", dataSourceName, activeTime,
StringUtils.leftPad(Integer.toString(inactiveMinute), 2, '0') + ":"
+ StringUtils.leftPad(Integer.toString(inactiveSecond), 2, '0'));
}
else if (scheduleType == TYPE_DAILY)
message = new TranslatableMessage("maintenanceEvents.schedule.dailyUntil", dataSourceName, activeTime(),
inactiveTime());
else if (scheduleType == TYPE_WEEKLY)
message = new TranslatableMessage("maintenanceEvents.schedule.weeklyUntil", dataSourceName, weekday(true),
activeTime(), weekday(false), inactiveTime());
else if (scheduleType == TYPE_MONTHLY)
message = new TranslatableMessage("maintenanceEvents.schedule.monthlyUntil", dataSourceName,
monthday(true), activeTime(), monthday(false), inactiveTime());
else if (scheduleType == TYPE_YEARLY)
message = new TranslatableMessage("maintenanceEvents.schedule.yearlyUntil", dataSourceName, monthday(true),
month(true), activeTime(), monthday(false), month(false), inactiveTime());
else if (scheduleType == TYPE_CRON)
message = new TranslatableMessage("maintenanceEvents.schedule.cronUntil", dataSourceName, activeCron,
inactiveCron);
else
throw new ShouldNeverHappenException("Unknown schedule type: " + scheduleType);
return message;
}
private TranslatableMessage getTypeMessage() {
switch (scheduleType) {
case TYPE_MANUAL:
return new TranslatableMessage("maintenanceEvents.type.manual");
case TYPE_HOURLY:
return new TranslatableMessage("maintenanceEvents.type.hour");
case TYPE_DAILY:
return new TranslatableMessage("maintenanceEvents.type.day");
case TYPE_WEEKLY:
return new TranslatableMessage("maintenanceEvents.type.week");
case TYPE_MONTHLY:
return new TranslatableMessage("maintenanceEvents.type.month");
case TYPE_YEARLY:
return new TranslatableMessage("maintenanceEvents.type.year");
case TYPE_ONCE:
return new TranslatableMessage("maintenanceEvents.type.once");
case TYPE_CRON:
return new TranslatableMessage("maintenanceEvents.type.cron");
}
return null;
}
private String activeTime() {
return StringUtils.leftPad(Integer.toString(activeHour), 2, '0') + ":"
+ StringUtils.leftPad(Integer.toString(activeMinute), 2, '0') + ":"
+ StringUtils.leftPad(Integer.toString(activeSecond), 2, '0');
}
private String inactiveTime() {
return StringUtils.leftPad(Integer.toString(inactiveHour), 2, '0') + ":"
+ StringUtils.leftPad(Integer.toString(inactiveMinute), 2, '0') + ":"
+ StringUtils.leftPad(Integer.toString(inactiveSecond), 2, '0');
}
private static final String[] weekdays = { "", "common.day.mon", "common.day.tue", "common.day.wed",
"common.day.thu", "common.day.fri", "common.day.sat", "common.day.sun" };
private TranslatableMessage weekday(boolean active) {
int day = activeDay;
if (!active)
day = inactiveDay;
return new TranslatableMessage(weekdays[day]);
}
private TranslatableMessage monthday(boolean active) {
int day = activeDay;
if (!active)
day = inactiveDay;
if (day == -3)
return new TranslatableMessage("common.day.thirdLast");
if (day == -2)
return new TranslatableMessage("common.day.secondLastLast");
if (day == -1)
return new TranslatableMessage("common.day.last");
if (day != 11 && day % 10 == 1)
return new TranslatableMessage("common.counting.st", Integer.toString(day));
if (day != 12 && day % 10 == 2)
return new TranslatableMessage("common.counting.nd", Integer.toString(day));
if (day != 13 && day % 10 == 3)
return new TranslatableMessage("common.counting.rd", Integer.toString(day));
return new TranslatableMessage("common.counting.th", Integer.toString(day));
}
private static final String[] months = { "", "common.month.jan", "common.month.feb", "common.month.mar",
"common.month.apr", "common.month.may", "common.month.jun", "common.month.jul", "common.month.aug",
"common.month.sep", "common.month.oct", "common.month.nov", "common.month.dec" };
private TranslatableMessage month(boolean active) {
int day = activeDay;
if (!active)
day = inactiveDay;
return new TranslatableMessage(months[day]);
}
@Override
public String getTypeKey() {
return "event.audit.maintenanceEvent";
}
public void validate(ProcessResult response) {
if (StringValidation.isLengthGreaterThan(alias, 50))
response.addContextualMessage("alias", "maintenanceEvents.validate.aliasTooLong");
if (dataSourceId <= 0)
response.addContextualMessage("dataSourceId", "validate.invalidValue");
// Check that cron patterns are ok.
if (scheduleType == TYPE_CRON) {
try {
new CronTimerTrigger(activeCron);
}
catch (Exception e) {
response.addContextualMessage("activeCron", "maintenanceEvents.validate.activeCron", e.getMessage());
}
try {
new CronTimerTrigger(inactiveCron);
}
catch (Exception e) {
response.addContextualMessage("inactiveCron", "maintenanceEvents.validate.inactiveCron", e.getMessage());
}
}
// Test that the triggers can be created.
MaintenanceEventRT rt = new MaintenanceEventRT(this);
try {
rt.createTrigger(true);
}
catch (RuntimeException e) {
response.addContextualMessage("activeCron", "maintenanceEvents.validate.activeTrigger", e.getMessage());
}
try {
rt.createTrigger(false);
}
catch (RuntimeException e) {
response.addContextualMessage("inactiveCron", "maintenanceEvents.validate.inactiveTrigger", e.getMessage());
}
// If the event is once, make sure the active time is earlier than the inactive time.
if (scheduleType == TYPE_ONCE) {
DateTime adt = new DateTime(activeYear, activeMonth, activeDay, activeHour, activeMinute, activeSecond, 0);
DateTime idt = new DateTime(inactiveYear, inactiveMonth, inactiveDay, inactiveHour, inactiveMinute,
inactiveSecond, 0);
if (idt.getMillis() <= adt.getMillis())
response.addContextualMessage("scheduleType", "maintenanceEvents.validate.invalidRtn");
}
}
@Override
public void addProperties(List<TranslatableMessage> list) {
AuditEventType.addPropertyMessage(list, "common.xid", xid);
AuditEventType.addPropertyMessage(list, "maintenanceEvents.dataSource", dataSourceId);
AuditEventType.addPropertyMessage(list, "maintenanceEvents.alias", alias);
AuditEventType.addPropertyMessage(list, "common.alarmLevel", AlarmLevels.getAlarmLevelMessage(alarmLevel));
AuditEventType.addPropertyMessage(list, "maintenanceEvents.type", getTypeMessage());
AuditEventType.addPropertyMessage(list, "common.disabled", disabled);
AuditEventType.addPropertyMessage(list, "common.configuration", getDescription());
}
@Override
public void addPropertyChanges(List<TranslatableMessage> list, MaintenanceEventVO from) {
AuditEventType.maybeAddPropertyChangeMessage(list, "common.xid", from.xid, xid);
AuditEventType.maybeAddPropertyChangeMessage(list, "maintenanceEvents.dataSource", from.dataSourceId,
dataSourceId);
AuditEventType.maybeAddPropertyChangeMessage(list, "maintenanceEvents.alias", from.alias, alias);
AuditEventType.maybeAddAlarmLevelChangeMessage(list, "common.alarmLevel", from.alarmLevel, alarmLevel);
if (from.scheduleType != scheduleType)
AuditEventType.addPropertyChangeMessage(list, "maintenanceEvents.type", from.getTypeMessage(),
getTypeMessage());
AuditEventType.maybeAddPropertyChangeMessage(list, "common.disabled", from.disabled, disabled);
if (from.activeYear != activeYear || from.activeMonth != activeMonth || from.activeDay != activeDay
|| from.activeHour != activeHour || from.activeMinute != activeMinute
|| from.activeSecond != activeSecond || from.activeCron != activeCron
|| from.inactiveYear != inactiveYear || from.inactiveMonth != inactiveMonth
|| from.inactiveDay != inactiveDay || from.inactiveHour != inactiveHour
|| from.inactiveMinute != inactiveMinute || from.inactiveSecond != inactiveSecond
|| from.inactiveCron != inactiveCron)
AuditEventType.maybeAddPropertyChangeMessage(list, "common.configuration", from.getDescription(),
getDescription());
}
//
//
// Serialization
//
@Override
public void jsonWrite(ObjectWriter writer) throws IOException, JsonException {
writer.writeEntry("xid", xid);
writer.writeEntry("dataSourceXid", dataSourceXid);
writer.writeEntry("alarmLevel", AlarmLevels.CODES.getCode(alarmLevel));
writer.writeEntry("scheduleType", TYPE_CODES.getCode(scheduleType));
}
@Override
public void jsonRead(JsonReader reader, JsonObject jsonObject) throws JsonException {
String text = jsonObject.getString("dataSourceXid");
if (text != null) {
DataSourceVO<?> ds = new DataSourceDao().getDataSource(text);
if (ds == null)
throw new TranslatableJsonException("emport.error.maintenanceEvent.invalid", "dataSourceXid", text);
dataSourceId = ds.getId();
}
text = jsonObject.getString("alarmLevel");
if (text != null) {
alarmLevel = AlarmLevels.CODES.getId(text);
if (!AlarmLevels.CODES.isValidId(alarmLevel))
throw new TranslatableJsonException("emport.error.maintenanceEvent.invalid", "alarmLevel", text,
AlarmLevels.CODES.getCodeList());
}
text = jsonObject.getString("scheduleType");
if (text != null) {
scheduleType = TYPE_CODES.getId(text);
if (!TYPE_CODES.isValidId(scheduleType))
throw new TranslatableJsonException("emport.error.maintenanceEvent.invalid", "scheduleType", text,
TYPE_CODES.getCodeList());
}
}
}